﻿using System;
using System.Collections.Generic;
using System.Linq;
using CLM.Gestao.Interfaces;
using CLM.Gestao.Administrador.DAL;
using CLM.Gestao.Importacao;

namespace CLM.Gestao.Administrador.BLL
{
    public class ContaContabil : ContaContabilBLO<TB_CONTA_CONTABIL>
    {
        #region Propriedades

        public int IdContaContabil { get; set; }
        public string NomeContaContabil { get; set; }
        public string LoginContaContabil { get; set; }
        public bool IndicaAtivo { get; set; }
        public bool? IndicaGerencial { get; set; }
        public PerfilAcesso PerfilAcesso { get; set; }

        #endregion Propriedades

        #region Conversões

        private List<TB_CONTA_CONTABIL> ConvertToEDM(List<Lista> pLista)
        {
            List<TB_CONTA_CONTABIL> List = (from l in pLista
                                            select new TB_CONTA_CONTABIL
                                            {
                                                ID_CONTA_CONTABIL = l.IdContaContabil,
                                                CODIGO_CONTA_CONTABIL = l.CodigoContaContabil.Trim(),
                                                DESCRICAO_CONTA_CONTABIL = l.DescricaoContaContabil.Trim(),
                                                ID_CATEGORIA = l.IdCategoria
                                            }).ToList();
            return List;
        }

        private List<Lista> ConvertToContext(IQueryable<TB_CONTA_CONTABIL> pContext)
        {
            List<Lista> List = (from l in pContext
                                select new Lista
                                {
                                    IdContaContabil = l.ID_CONTA_CONTABIL,
                                    CodigoContaContabil = l.CODIGO_CONTA_CONTABIL.Trim(),
                                    DescricaoContaContabil = l.DESCRICAO_CONTA_CONTABIL.Trim(),
                                    IdCategoria = l.ID_CATEGORIA,
                                    NomeCategoria = l.TB_CATEGORIA.NOME_CATEGORIA,
                                    CodigoCategoria = l.TB_CATEGORIA.CODIGO_CATEGORIA
                                }).ToList();
            return List;
        }

        #endregion Conversões

        #region CRUD

        /// <summary>
        /// Adiciona
        /// </summary>
        /// <param name="pLista">pLista</param>
        public void Add(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Add(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// Deleta
        /// </summary>
        /// <param name="pLista">pLista</param>
        public void Delete(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Delete(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// Atualiza
        /// </summary>
        /// <param name="pLista">pLista</param>
        public void Update(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Update(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// Efetua a pesquisa na tabela, filtrando as solicitações do usuário
        /// </summary>
        /// <param name="pPesquisa">pPesquisa</param>
        /// <returns>Lista</returns>
        private IQueryable<TB_CONTA_CONTABIL> Get(Lista pPesquisa)
        {
            IQueryable<TB_CONTA_CONTABIL> Consulta;

            Consulta = from p in GetAll()
                       select p;
            if (pPesquisa != null)
            {
                if (!string.IsNullOrWhiteSpace(pPesquisa.CodigoContaContabil))
                    Consulta = from p in Consulta where p.CODIGO_CONTA_CONTABIL.Contains(pPesquisa.CodigoContaContabil) select p;

                if (!string.IsNullOrWhiteSpace(pPesquisa.DescricaoContaContabil))
                    Consulta = from p in Consulta where p.DESCRICAO_CONTA_CONTABIL.Contains(pPesquisa.DescricaoContaContabil) select p;

                if (pPesquisa.IdCategoria > 0)
                    Consulta = from p in Consulta where p.TB_CATEGORIA.NOME_CATEGORIA == pPesquisa.NomeCategoria select p;

                if (pPesquisa.IndicaGerencial == true)
                    Consulta = from p in Consulta where p.INDICA_GERENCIAL == pPesquisa.IndicaGerencial select p;

                if (pPesquisa.IndicaGerencial == false)
                    Consulta = from p in Consulta where p.INDICA_GERENCIAL != true select p;
            }
            return Consulta;
        }

        #endregion CRUD

        #region Retorna Listas e Itens

        public List<Lista> GetLista()
        {
            return GetLista(new Lista());
        }

        public List<Lista> GetLista(Lista pPesquisa)
        {
            var ListaContaContabil = ConvertToContext(Get(pPesquisa));
            return ListaContaContabil;
        }

        public List<Itens> GetItens()
        {
            return GetItens(new Lista());
        }

        public List<Itens> GetItens(Lista pPesquisa)
        {
            List<Itens> ListaContaContabil = (from p in Get(pPesquisa)
                                              select new Itens()
                                              {
                                                  IdContaContabil = p.ID_CONTA_CONTABIL,
                                                  DescricaoContaContabil = p.DESCRICAO_CONTA_CONTABIL,
                                                  CodigoContaContabil = p.CODIGO_CONTA_CONTABIL
                                              }).ToList();
            return ListaContaContabil;
        }

        #endregion Retorna Listas e Itens

        #region Consultas


        #endregion Consultas

        #region Processos

        #region Sincroniza Contas Contabeis

        public void SincronizaContasContabeis()
        {
            
            OnImportData(this, new ImportDataEventArgs("Acessando Banco de Dados Protheus..."));

            Protheus.BLL.SpContaContabilGaldermaNacional ProcedureContaContabil = new Protheus.BLL.SpContaContabilGaldermaNacional();
            List<Lista> ContasProtheus = (from p in ProcedureContaContabil.ExecutaProcedure()
                                           select new Lista
                                           {
                                               DescricaoContaContabil = p.DescricaoContaContabil,
                                               CodigoContaContabil = p.CodigoContaContabil,
                                               CodigoCategoria = p.CodigoSubConta,
                                               NomeCategoria = p.DescricaoSubConta
                                           }).ToList();

            OnImportData(this, new ImportDataEventArgs("Acessando informações de Contas Contábeis..."));

            AtualizaCategorias(ContasProtheus.GroupBy(g => new { CodigoCategoria = g.CodigoCategoria, NomeCategoria = g.NomeCategoria}).Select(s => new Categoria.Itens(){ CodigoCategoria = s.Key.CodigoCategoria, NomeCategoria = s.Key.NomeCategoria}).ToList());
            
            Categoria Categoria = new Categoria();
            var Categorias = Categoria.GetItens().ToDictionary(d => d.CodigoCategoria);

            List<Lista> ContasSQL = (from p in Get(new Lista())
                                      select new Lista
                                      {
                                          CodigoContaContabil = p.CODIGO_CONTA_CONTABIL,
                                          DescricaoContaContabil = p.DESCRICAO_CONTA_CONTABIL,
                                          IdContaContabil = p.ID_CONTA_CONTABIL,
                                          IdCategoria = p.ID_CATEGORIA,
                                          CodigoCategoria = p.TB_CATEGORIA.CODIGO_CATEGORIA,
                                          NomeCategoria = p.TB_CATEGORIA.NOME_CATEGORIA,
                                          IndicaGerencial = p.INDICA_GERENCIAL
                                      }).ToList();

            var Novos = (from pr in ContasProtheus
                         join db in ContasSQL
                         on pr.CodigoContaContabil equals db.CodigoContaContabil
                         into centros
                         from db in centros.DefaultIfEmpty()
                         where db == null
                         select new Lista
                         {
                             CodigoContaContabil = pr.CodigoContaContabil,
                             DescricaoContaContabil = pr.DescricaoContaContabil,
                             IdCategoria = Categorias[pr.CodigoCategoria].IdCategoria,
                             IndicaGerencial = false
                         }).ToList();

            int i = 0;
            if (Novos != null && Novos.Count() > 0)
            {
                foreach (var item in Novos)
                {
                    this.Add(new TB_CONTA_CONTABIL() { CODIGO_CONTA_CONTABIL = item.CodigoContaContabil, DESCRICAO_CONTA_CONTABIL = item.DescricaoContaContabil, ID_CATEGORIA = item.IdCategoria, INDICA_GERENCIAL = false, INDICA_ATIVO = true });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Incluindo Contas Contábeis...", i, Novos.Count()));
                }
            }

            List<Lista> Alterados = (from pr in ContasProtheus
                                     join db in ContasSQL
                                     on pr.CodigoContaContabil equals db.CodigoContaContabil
                                     where (pr.DescricaoContaContabil != db.DescricaoContaContabil || pr.CodigoCategoria != db.CodigoCategoria)
                                     select new Lista
                                     {
                                         IdContaContabil = db.IdContaContabil,
                                         IdCategoria = Categorias[pr.NomeCategoria].IdCategoria,
                                         CodigoContaContabil = pr.CodigoContaContabil,
                                         DescricaoContaContabil = pr.DescricaoContaContabil,
                                         IndicaGerencial = false
                                     }).ToList();
            i = 0;
            if (Alterados != null && Alterados.Count() > 0)
            {
                foreach (var item in Alterados)
                {
                    this.Update(new TB_CONTA_CONTABIL() { ID_CONTA_CONTABIL = item.IdContaContabil, CODIGO_CONTA_CONTABIL = item.CodigoContaContabil, DESCRICAO_CONTA_CONTABIL = item.DescricaoContaContabil, ID_CATEGORIA = item.IdCategoria, INDICA_GERENCIAL = item.IndicaGerencial, INDICA_ATIVO = true });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Alterando Contas Contábeis...", i, Alterados.Count()));
                }
            }

            if (Novos != null && Novos.Count() == 0 && Alterados != null && Alterados.Count() == 0)
                OnImportData(this, new ImportDataEventArgs("As Contas Contábeis já estão sincronizadas. Não foram realizadas alterações."));
            else
                OnImportData(this, new ImportDataEventArgs("Sincronização efetuada com sucesso."));
        }

        private void AtualizaCategorias(List<Categoria.Itens> pCategorias)
        {
            Categoria Categoria = new Categoria();
            OnImportData(this, new ImportDataEventArgs("Importando informações de Sub Grupos..."));

            List<Categoria.Itens> CategoriasSQL = (from p in Categoria.GetAll()
                                                      select new Categoria.Itens
                                                      {
                                                          IdCategoria = p.ID_CATEGORIA,
                                                          CodigoCategoria = p.CODIGO_CATEGORIA,
                                                          NomeCategoria = p.NOME_CATEGORIA
                                                      }).ToList();

            // Compara os arquivos do Excel com os existentes no Banco de Dados
            var Novos = (from p in pCategorias
                         join db in CategoriasSQL
                         on p.CodigoCategoria equals db.CodigoCategoria
                         into categorias
                         from db in categorias.DefaultIfEmpty()
                         where db == null
                         select new Categoria.Itens
                         {
                             CodigoCategoria = p.CodigoCategoria,
                             NomeCategoria = p.NomeCategoria
                         }).ToList();

            int i = 0;
            if (Novos != null && Novos.Count() > 0)
            {
                foreach (var item in Novos)
                {
                    Categoria.Add(new TB_CATEGORIA() { NOME_CATEGORIA = item.NomeCategoria, CODIGO_CATEGORIA = item.CodigoCategoria });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Incluindo Sub Grupos...", i, Novos.Count()));
                }
            }

            List<Categoria.Itens> Alterados = (from pr in pCategorias
                                     join db in CategoriasSQL
                                     on pr.CodigoCategoria equals db.CodigoCategoria
                                     where pr.NomeCategoria != db.NomeCategoria
                                     select new Categoria.Itens
                                     {
                                         IdCategoria = db.IdCategoria,
                                         CodigoCategoria = pr.CodigoCategoria,
                                         NomeCategoria = pr.NomeCategoria
                                     }).ToList();
            i = 0;
            if (Alterados != null && Alterados.Count() > 0)
            {
                foreach (var item in Alterados)
                {
                    Categoria.Update(new TB_CATEGORIA() { ID_CATEGORIA = item.IdCategoria, CODIGO_CATEGORIA = item.CodigoCategoria, NOME_CATEGORIA = item.NomeCategoria });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Alterando Sub Grupos...", i, Alterados.Count()));
                }
            }

            if (Novos != null && Novos.Count() == 0 && Alterados != null && Alterados.Count() == 0)
                OnImportData(this, new ImportDataEventArgs("Os Sub Grupos já estão sincronizadas. Não foram realizadas alterações."));
            else
                OnImportData(this, new ImportDataEventArgs("Sincronização efetuada com sucesso."));
        }

        #endregion Sincroniza Contas Contabeis

        #endregion

        #region Classes

        /// <summary>
        /// Preenche o combobox
        /// </summary>
        public class Itens
        {
            public int IdContaContabil { get; set; }
            public string CodigoContaContabil { get; set; }
            public string DescricaoContaContabil { get; set; }
        }

        /// <summary>
        /// Lista com as propriedades da classe
        /// </summary>
        public class Lista
        {
            public int IdContaContabil { get; set; }
            public string CodigoContaContabil { get; set; }
            public string DescricaoContaContabil { get; set; }
            public int? IdCategoria { get; set; }
            public bool? IndicaGerencial { get; set; }
            public string NomeCategoria { get; set; }
            public string CodigoCategoria { get; set; }
        }

        #endregion Classes

        public event ImportDataEventHandler OnImportData;
    }

    public class ContaContabilBLO<T> : IBLO<T> where T : TB_CONTA_CONTABIL
    {
        /// <summary>
        /// ContaContabilDAO
        /// </summary>
        private ContaContabilDAO<T> _base = new ContaContabilDAO<T>();

        /// <summary>
        /// Adiciona uma entidade no sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        /// <returns>Entidade</returns>
        public T Add(T pEntity)
        {
            Validate(pEntity);
            _base.Add(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// Salva uma entidade do sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        /// <returns>Entidade</returns>
        public T Save(T pEntity)
        {
            Validate(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// Deleta uma entidade do sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        public void Delete(T pEntity)
        {
            _base.Delete(pEntity);
            _base.Save(pEntity);
        }

        /// <summary>
        /// Atualiza uma entidade do sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        /// <returns>Entidade</returns>
        public T Update(T pEntity)
        {
            Validate(pEntity);
            _base.Update(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// Recupera uma entidade do sistema
        /// </summary>
        /// <returns>Lista</returns>
        public IQueryable<T> GetAll()
        {
            return _base.GetAll();
        }

        /// <summary>
        /// Valida a consistência da Entidade antes de 'comitar' a transação
        /// </summary>
        /// <param name="pEntity">Entidade</param>
        public void Validate(T pEntity)
        {
            int validate = (from u in _base.GetAll()
                                  where (u.CODIGO_CONTA_CONTABIL == pEntity.CODIGO_CONTA_CONTABIL && u.ID_CONTA_CONTABIL != pEntity.ID_CONTA_CONTABIL)
                                  select u).Count();

            if (validate > 0)
                throw new Exception("Já existe uma conta contábil cadastrada com esse código.");

            validate = (from u in _base.GetAll()
                        where ((u.DESCRICAO_CONTA_CONTABIL == pEntity.DESCRICAO_CONTA_CONTABIL && u.CODIGO_CONTA_CONTABIL == pEntity.CODIGO_CONTA_CONTABIL) && u.ID_CONTA_CONTABIL != pEntity.ID_CONTA_CONTABIL)
                            select u).Count();

            if (validate > 0)
                throw new Exception("Já existe uma conta contábil cadastrada com esse nome.");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pEntity"></param>
        public void ValidateDelete(T pEntity)
        {
            //int Perfilvalidate = (from u in _base.GetAll()
            //                      where u.TB_USUARIO.Where(w => w.ID_PERFIL_ACESSO == pEntity.ID_PERFIL_ACESSO).Select(s => s).Count() > 0
            //                      select u).Count();

            //if (Perfilvalidate > 0)
            //    throw new Exception("Existem usuários relacionados com este perfil de acesso.\nA operação não pôde ser concluída");
        }
    }
}